from random import randint
from tkinter import Tk, Frame, Label, Entry, Text, Button, IntVar, Radiobutton, Checkbutton, messagebox, StringVar


def completion(matrix_half):  # 补全半个矩阵
    length = len(matrix_half)
    for i in range(length):
        for j in range(length):
            if matrix_half[i][j] == 1 and matrix_half[j][i] != 1:
                matrix_half[i][j] = 1 / matrix_half[j][i]
            if i == j:
                matrix_half[i][j] = 1
    return matrix_half


def matrix_transpose(matrix):  # 矩阵转置
    return [[matrix[j][i] for j in range(len(matrix))] for i in range(len(matrix[0]))]


def matrix_multiplication(matrix_2, matrix_1):  # 二维与一维的矩阵乘法
    return [sum(matrix_2[j][i] * matrix_1[i] for i in range(len(matrix_1))) for j in range(len(matrix_2))]


def summation(matrix_b):  # 求和法
    matrix_b = matrix_transpose(matrix_b)
    b_ = [[i for i in j] for j in matrix_b]  # 深度copy 等同于 b = copy.deepcopy(matrix_b)
    matrix_b = [[matrix_b[i][j] / sum(b_[i]) for j in range(len(matrix_b))] for i in range(len(matrix_b[0]))]  # 归一化
    matrix_b = matrix_transpose(matrix_b)
    v = [sum(i) for i in matrix_b]  # 按行求和得到v
    w = [i / sum(v) for i in v]  # 归一化得到w
    return v, w


def rooting(matrix_b):  # 求根法
    def q_root(list0):  # 相乘再开方
        quadrature = 1
        for i in list0:
            quadrature *= i
        root_ = quadrature ** (1 / len(list0))
        return root_

    v = [q_root(i) for i in matrix_b]  # 按行相乘再开方
    w = [i / sum(v) for i in v]  # 归一化得到w
    return v, w


def check(matrix_a):  # 一致性检验,1求根法，0求和法
    ri = {1: 0, 2: 0, 3: 0.58, 4: 0.92, 5: 1.12, 6: 1.24, 7: 1.32, 8: 1.41, 9: 1.45}  # RI表
    weight = rooting(matrix_a)[1] if method == 1 else summation(matrix_a)[1]
    a_w = matrix_multiplication(matrix_a, weight)
    lambda_max = sum([a_w[i] / weight[i] for i in range(len(weight))]) / len(a_w)
    if len(a_w) > 1:
        ci = (lambda_max - len(a_w)) / (len(a_w) - 1)  # CI值
    else:
        ci = 0
    ri = ri[len(a_w)]  # RI值
    if ri != 0:
        cr = ci / ri  # CR值
    else:
        cr = 0
    best = weight.index(max(weight))
    return a_w, weight, lambda_max, ci, ri, cr, best, True if cr < 0.1 else False


def check_all(matrix_, matrix_s):  # 层次总排序组合一致性检验
    w_ = check(matrix_)[0]  # 上一层的一致性检验值
    ci = sum([w_[i] * check(matrix_s[i])[3] for i in range(len(matrix_s))])
    ri = sum([w_[i] * check(matrix_s[i])[4] for i in range(len(matrix_s))])  # 这一层的一致性检验值权重和
    if ri != 0:
        cr = ci / ri
    else:
        cr = 0
    return cr, True if cr < 0.1 else False


def choice_best(target_, programmes_):
    target_w = check(target_)[1]
    programmes_w = matrix_transpose([check(i)[1] for i in programmes_])
    after_w = matrix_multiplication(programmes_w, target_w)
    best = after_w.index(max(after_w))
    cr, check_ = check_all(target_, programmes_)
    return target_w, programmes_w, after_w, best, cr, check_


def ahp_gui():
    root_new = Tk()
    root_new.title('AHP计算  by：wld')
    frame_first = Frame(root_new)
    frame_first.pack(side='top')
    var_o_s, var_w_s = StringVar(), StringVar()
    Label(frame_first, text='准则数：', font=('宋体', 15)).pack(side='left')
    entry001 = Entry(frame_first, textvariable=var_o_s, font=("宋体", 15), fg='blue', width=3, justify='center')
    var_o_s.set('4')
    entry001.pack(side='left')
    Label(frame_first, text='方案数：', font=('宋体', 15)).pack(side='left')
    entry002 = Entry(frame_first, textvariable=var_w_s, font=("宋体", 15), fg='blue', width=3, justify='center')
    var_w_s.set('5')
    entry002.pack(side='left')

    def third():
        option = int(eval(var_o_s.get()))  # 准则数
        ways = int(eval(var_w_s.get()))  # 方案数
        random_ = check_random.get()
        t_ = ['目标'] + ['目标']
        t_o = ['准则'] + [i.get() for i in var_o_names]
        o_w = ['方案'] + [i.get() for i in var_w_names]
        f = ['1', '1/2', '2', '1/3', '3', '1/4', '4', '1/5', '5', '1/6', '6', '1/7', '7', '1/8', '8', '1/9', '9',
             '1/10',
             '10', '1', '1/2', '2', '1/3', '3', '1/4', '4', '1/5', '5', '1/6', '6', '1/7', '7', '1/8', '8', '1/9', '9']

        def table(u, d):
            frame_n_t = Frame(frame_show)
            frame_n_t.pack(side='top')
            for one in range(len(u) - 1):
                frame_one = Frame(frame_n_t)
                frame_one.pack(side='left')
                frame_n = Frame(frame_one)
                frame_n.pack(side='top')
                num = [[Entry(frame_n, width=6, justify='center', font=("宋体", 15)) for _ in range(len(d))] for _ in d]
                for row in range(len(num)):
                    for column in range(len(num[0])):
                        if row == 0 and column == 0:
                            num[row][column].insert('end', u[one + 1])
                            num[row][column].config(state='disabled', bg='gray')
                        elif row == 0 and column != 0:
                            num[row][column].insert('end', d[column])
                            num[row][column].config(state='disabled', bg='gray')
                        elif row != 0 and column == 0:
                            num[row][column].insert('end', d[row])
                            num[row][column].config(state='disabled', bg='gray')
                        elif row < column:
                            num[row][column].insert('end',
                                                    str(f[(randint(0, int(max(ways, option))))]) if random_ else '1')
                        else:
                            num[row][column].insert('end', '1')
                            num[row][column].config(state='disabled', bg='gray')
                        num[row][column].grid(row=row, column=column)
                all_num.append(num)

        table(t_, t_o)
        if len(o_w) > 1:
            table(t_o, o_w)
        check_r.config(state='disabled')
        for i in entry003 + entry004:
            i.config(state='disabled')

        button001.config(state='disabled')
        frame_s = Frame(frame_show)
        frame_s.pack(side='top')
        Label(frame_s, text='计算方法：', font=('宋体', 15)).pack(side='left')
        global m
        m = IntVar()
        radio3_2 = Radiobutton(frame_s, text='方根法', font=('宋体', 15), variable=m, value=1)
        radio3_2.pack(side='left')
        radio3_1 = Radiobutton(frame_s, text='和积法', font=('宋体', 15), variable=m, value=0)
        radio3_1.pack(side='left')
        radio3_2.select()
        # 小数
        global entry_small_num
        Label(frame_s, text='显示小数：', font=('宋体', 15)).pack(side='left')
        entry_small_num = Entry(frame_s, font=("宋体", 15), fg='blue', width=3, justify='center')
        entry_small_num.insert(0, '4')
        entry_small_num.pack(side='left')
        Label(frame_s, text='位', font=('宋体', 15)).pack(side='left')
        Button(frame_show, text="计算", command=solve, font=('宋体', 15)).pack(side='bottom')

    def second():
        frame_second = Frame(root_new)
        frame_second.pack(side='top')
        global var_o_names, var_w_names, check_random, check_r, entry003, entry004, button001
        var_o_names = [StringVar() for _ in range(int(eval(var_o_s.get())))]
        var_w_names = [StringVar() for _ in range(int(eval(var_w_s.get())))]
        Label(frame_second, text='准则名', font=('宋体', 15)).pack(side='top')
        entry003 = []
        for i in range(len(var_o_names)):
            var_o_names[i].set(f'准则{i + 1}')
            entry003.append(Entry(frame_second, textvariable=var_o_names[i], font=("宋体", 15), fg='blue',
                                  width=10, justify='center'))
        for i in entry003:
            i.pack(side='left')
        frame_second_ = Frame(root_new)
        frame_second_.pack(side='top')
        Label(frame_second_, text='方案名', font=('宋体', 15)).pack(side='top')
        entry004 = []
        for i in range(len(var_w_names)):
            var_w_names[i].set(f'方案{i + 1}')
            entry004.append(Entry(frame_second_, textvariable=var_w_names[i], font=("宋体", 15), fg='blue',
                                  width=10, justify='center'))
        for i in entry004:
            i.pack(side='left')
        frame_second__ = Frame(root_new)
        frame_second__.pack(side='top')
        check_random = IntVar()
        check_r = Checkbutton(frame_second__, text='随机初始化数据', variable=check_random, font=('宋体', 12))
        check_r.pack(side='left')
        button001 = Button(frame_second__, text="下一步", command=third, font=('宋体', 15))
        button001.pack(side='left')

    def first():
        o_s = int(eval(var_o_s.get()))
        w_s = int(eval(var_w_s.get()))
        var_o_s.set(str(o_s))
        var_w_s.set(str(w_s))
        if (o_s <= 0) or (w_s < 0) or (o_s > 9) or (w_s > 9):
            messagebox.showerror(title='错误！', message='方案数和准则数限定取值0-9!')
            exit(0)
        if o_s * (w_s + 1) > 36:
            messagebox.showinfo(title='提示！',
                                message='准则和方案太多了！\n你的屏幕太小显示不过来！\n倒是也能算，只是你看不见我也没办法！')
        second()
        button0.config(state='disabled')
        entry001.config(state='disabled')
        entry002.config(state='disabled')

    button0 = Button(frame_first, text="下一步", command=first, font=('宋体', 15))
    button0.pack(side='left')

    # 整体展示
    frame_show = Frame(root_new)
    frame_show.pack(side='bottom')
    all_num = []

    def solve():
        global m, method
        method = int(m.get())
        option = int(eval(var_o_s.get()))  # 准则数
        ways = int(eval(var_w_s.get()))  # 方案数
        round_ = int(entry_small_num.get())
        t_ = ['目标'] + ['目标']
        t_o = ['准则'] + [i.get() for i in var_o_names]
        o_w = ['方案'] + [i.get() for i in var_w_names]
        names = ['目标/准则'] + [f'准则{i + 1}/方案' for i in range(option)]
        root1 = Tk()
        root1.title('运算结果  by：wld')
        frame_show1 = Frame(root1)
        frame_show1.pack(side='top')
        number = [[[eval(i.get()) for i in j[1:]] for j in k[1:]] for k in all_num]
        number = [completion(i) for i in number]
        numbers = []
        s = {0: '0', 1: '1', 2: '2', 3: '3', 4: '4', 5: '5', 6: '6', 7: '7', 8: '8', 9: '9', 10: '10', 11: '11',
             12: '12', 13: '13', 14: '14', 15: '15', 16: '16', 17: '17', 18: '18', 19: '19', 20: '20', 21: '21',
             1 / 2: "1/2", 1 / 3: '1/3', 1 / 4: '1/4', 1 / 5: '1/5', 1 / 6: '1/6', 1 / 7: '1/7', 1 / 8: '1/8',
             1 / 9: '1/9', 1 / 10: '1/10', 1 / 11: '1/11', 1 / 12: '1/12', 1 / 13: '1/13', 1 / 14: '1/14',
             1 / 15: '1/15', 1 / 16: '1/16', 1 / 17: '1/17', 1 / 18: '1/18', 1 / 19: '1/19', 1 / 20: '1/20',
             1 / 21: '1/21'}

        def table1(u, d, h=0):
            frame_n_t1 = Frame(frame_show1)
            frame_n_t1.pack(side='top')
            for one in range(len(u) - 1):
                frame_one = Frame(frame_n_t1)
                frame_one.pack(side='left')
                frame_nums = Frame(frame_one)
                frame_nums.pack(side='top')
                frame_t = Frame(frame_one)
                frame_t.pack(side='left')
                nu = [[Entry(frame_nums, width=6, justify='center', font=("宋体", 15)) for _ in range(len(d))] for _ in
                      d]
                for row in range(len(nu)):
                    for column in range(len(nu[0])):
                        if row == 0 and column == 0:
                            nu[row][column].insert('end', u[one + 1])
                        elif row == 0 and column != 0:
                            nu[row][column].insert('end', d[column])
                        elif row != 0 and column == 0:
                            nu[row][column].insert('end', d[row])
                        else:
                            numb = (number[h][row - 1][column - 1])
                            try:
                                nu[row][column].insert('end', str(s[numb]))
                            except:
                                nu[row][column].insert('end', str(number[h][row - 1][column - 1]))
                        nu[row][column].grid(row=row, column=column)
                h += 1
                if h == 1:
                    global text0
                    text0 = Text(frame_t, font=("宋体", 15), width=6 * (len(d)), height=2 * (len(d) + 1))
                    text0.config(foreground='green')
                    text0.pack(side='right')
                text = Text(frame_t, font=("宋体", 15), width=6 * (len(d)), height=2 * (len(d) + 1))
                number0 = [[float(eval(i.get())) for i in j[1:]] for j in nu[1:]]
                numbers.append(number0)
                tip0 = ['A_w:', 'Weight:', 'λ_max:', 'CI:', 'RI:', 'CR:', 'Best:', '一致性检验:']
                info0 = check(number0)
                if h == 1:
                    text.insert('end', f'{names[0]}及一致性检验：' + '\n')
                else:
                    text.insert('end', f'{names[h - 1]}及一致性检验：' + '\n')
                for i in range(8):
                    if isinstance(info0[i], list):
                        text.insert('end', tip0[i] + str([round(j, round_) for j in info0[i]]) + '\n')
                    else:
                        if tip0[i] == 'Best:':
                            text.insert('end', tip0[i] + str(d[int(info0[i]) + 1]) + '\n')
                        else:
                            text.insert('end', tip0[i] + str(info0[i]) + '\n')
                if not info0[-1]:
                    text.config(foreground='red')
                text.pack()

        table1(t_, t_o)
        if len(o_w) > 1:
            table1(t_o, o_w, h=1)
        print()
        tip = ['W_0:', 'W_1:', 'W_2:', 'Best:', 'CR:', '整体一致性检验:']
        info = choice_best(numbers[0], numbers[1:])
        text0.insert('end', '最终结果及整体一致性检验：' + '\n')
        for i in range(6):
            if isinstance(info[i], list):
                if not isinstance(info[i][0], list):
                    text0.insert('end', tip[i] + str([round(j, round_) for j in info[i]]) + '\n')
                else:
                    text0.insert('end', tip[i] + str([[round(k, round_) for k in j] for j in info[i]]) + '\n')
            else:
                if tip[i] == 'Best:':
                    text0.insert('end', tip[i] + str(o_w[int(info[i]) + 1]) + '\n')
                else:
                    text0.insert('end', tip[i] + str(info[i]) + '\n')
        if not info[-1]:
            text0.config(foreground='red')
        text0.pack(side='right')

    root_new.mainloop()

ahp_gui()
